package ledger

import (
	"context"
	"fmt"
	"gitee.com/wayjin/fabricsdk-go/cryptoutil"
	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/core/scc/qscc"
	"github.com/hyperledger/fabric/protos/common"
	pb "github.com/hyperledger/fabric/protos/peer"
	putils "github.com/hyperledger/fabric/protos/utils"
	"github.com/pkg/errors"
	"strconv"
)

const (
	QSCC_GetBlockByHash = "GetBlockByHash"
	QSCC_GetBlockByNumber = "GetBlockByNumber"
	QSCC_GetBlockByTxID = "GetBlockByTxID"
	QSCC_GetTransactionByTxID = "GetTransactionByID"
)

func getLedgerChaincodeSpecByHash(channelID string, blockHash []byte) *pb.ChaincodeInvocationSpec {
	input := &pb.ChaincodeInput{Args: [][]byte{[]byte(qscc.GetBlockByHash), []byte(channelID), blockHash}}

	spec := &pb.ChaincodeSpec{
		Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]),
		ChaincodeId: &pb.ChaincodeID{Name: "qscc"},
		Input:       input,
	}
	return &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
}

func getLedgerChaincodeSpecByNumber(channelID string, number int64) *pb.ChaincodeInvocationSpec {
	sNumber := strconv.FormatInt(number, 10)
	input := &pb.ChaincodeInput{Args: [][]byte{[]byte(qscc.GetBlockByNumber), []byte(channelID), []byte(sNumber)}}
	spec := &pb.ChaincodeSpec{
		Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]),
		ChaincodeId: &pb.ChaincodeID{Name: "qscc"},
		Input:       input,
	}
	return &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
}

func getLedgerChaincodeSpecByTxID(channelID string, txID string) *pb.ChaincodeInvocationSpec {
	input := &pb.ChaincodeInput{Args: [][]byte{[]byte(qscc.GetBlockByTxID), []byte(channelID), []byte(txID)}}
	spec := &pb.ChaincodeSpec{
		Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]),
		ChaincodeId: &pb.ChaincodeID{Name: "qscc"},
		Input:       input,
	}
	return &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
}

func getLedgerTransactionSpecByTxID(channelID string, txID string) *pb.ChaincodeInvocationSpec {
	input := &pb.ChaincodeInput{Args: [][]byte{[]byte(qscc.GetTransactionByID), []byte(channelID), []byte(txID)}}
	spec := &pb.ChaincodeSpec{
		Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]),
		ChaincodeId: &pb.ChaincodeID{Name: "qscc"},
		Input:       input,
	}
	return &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
}

func GetBlockByHash(signer cryptoutil.Signer, endorser pb.EndorserClient, channelID string, blockHash []byte) (*common.Block, error) {
	invocation := getLedgerChaincodeSpecByHash(channelID, blockHash)
	creator, err := signer.Serialize()
	if err != nil {
		return nil, err
	}

	prop, txid, err := putils.CreateChaincodeProposalWithTxIDAndTransient(common.HeaderType_ENDORSER_TRANSACTION, channelID, invocation, creator, "", nil)
	if err != nil {
		return nil, errors.WithMessage(err, fmt.Sprintf("error creating proposal for %s", qscc.GetBlockByHash))
	}

	signedProp, err := cryptoutil.GetSignedProposal(prop, signer)
	if err != nil {
		return nil, errors.WithMessage(err, fmt.Sprintf("error creating signed proposal for %s", qscc.GetBlockByHash))
	}

	resp, err := endorser.ProcessProposal(context.Background(), signedProp)
	if err != nil {
		return nil, err
	}
	fmt.Println("txid:", txid)
	if resp.Response.Status >= shim.ERRORTHRESHOLD {
		return nil, errors.New("invalid response status: " + resp.Response.Message)
	}

	block := &common.Block{}
	err = proto.Unmarshal(resp.Response.Payload, block)
	if err != nil {
		return nil, err
	}

	return block, nil
}

// GetBlockByNumber 根据区块高度查询区块
func GetBlockByNumber(signer cryptoutil.Signer, endorser pb.EndorserClient, channelID string, number int64) (*common.Block, error) {
	invocation := getLedgerChaincodeSpecByNumber(channelID, number)
	creator, err := signer.Serialize()
	if err != nil {
		return nil, err
	}

	prop, txid, err := putils.CreateChaincodeProposalWithTxIDAndTransient(common.HeaderType_ENDORSER_TRANSACTION, channelID, invocation, creator, "", nil)
	if err != nil {
		return nil, errors.WithMessage(err, fmt.Sprintf("error creating proposal for %s", qscc.GetBlockByHash))
	}

	signedProp, err := cryptoutil.GetSignedProposal(prop, signer)
	if err != nil {
		return nil, errors.WithMessage(err, fmt.Sprintf("error creating signed proposal for %s", qscc.GetBlockByHash))
	}

	fmt.Println("TxID: ", txid)
	resp, err := endorser.ProcessProposal(context.Background(), signedProp)
	if err != nil {
		return nil, err
	}

	if resp.Response.Status >= shim.ERRORTHRESHOLD {
		return nil, errors.New("invalid response status: " + resp.Response.Message)
	}

	block := &common.Block{}
	err = proto.Unmarshal(resp.Response.Payload, block)
	if err != nil {
		return nil, err
	}

	return block, nil
}

// GetBlockByTxID 根据交易ID获取区块信息
func GetBlockByTxID(signer cryptoutil.Signer, endorser pb.EndorserClient, channelID, txID string) (*common.Block, error) {
	invocation := getLedgerChaincodeSpecByTxID(channelID, txID)
	creator, err := signer.Serialize()
	if err != nil {
		return nil, err
	}

	prop, txid, err := putils.CreateChaincodeProposalWithTxIDAndTransient(common.HeaderType_ENDORSER_TRANSACTION, channelID, invocation, creator, "", nil)
	if err != nil {
		return nil, errors.WithMessage(err, fmt.Sprintf("error creating proposal for %s", qscc.GetBlockByHash))
	}

	signedProp, err := cryptoutil.GetSignedProposal(prop, signer)
	if err != nil {
		return nil, errors.WithMessage(err, fmt.Sprintf("error creating signed proposal for %s", qscc.GetBlockByTxID))
	}

	fmt.Println("TxID: ", txid)
	resp, err := endorser.ProcessProposal(context.Background(), signedProp)
	if err != nil {
		return nil, err
	}

	if resp.Response.Status >= shim.ERRORTHRESHOLD {
		return nil, errors.New("invalid response status: " + resp.Response.Message)
	}

	block := &common.Block{}
	err = proto.Unmarshal(resp.Response.Payload, block)
	if err != nil {
		return nil, err
	}

	return block, nil
}

// GetTransactionByID 根据交易ID查询交易
func GetTransactionByID(signer cryptoutil.Signer, endorser pb.EndorserClient, channelID, txID string) (*common.Block, error) {
	invocation := getLedgerTransactionSpecByTxID(channelID, txID)
	creator, err := signer.Serialize()
	if err != nil {
		return nil, err
	}

	prop, txid, err := putils.CreateChaincodeProposalWithTxIDAndTransient(common.HeaderType_ENDORSER_TRANSACTION, channelID, invocation, creator, "", nil)
	if err != nil {
		return nil, errors.WithMessage(err, fmt.Sprintf("error creating proposal for %s", qscc.GetBlockByHash))
	}

	signedProp, err := cryptoutil.GetSignedProposal(prop, signer)
	if err != nil {
		return nil, errors.WithMessage(err, fmt.Sprintf("error creating signed proposal for %s", qscc.GetBlockByTxID))
	}

	fmt.Println("TxID: ", txid)
	resp, err := endorser.ProcessProposal(context.Background(), signedProp)
	if err != nil {
		return nil, err
	}

	if resp.Response.Status >= shim.ERRORTHRESHOLD {
		return nil, errors.New("invalid response status: " + resp.Response.Message)
	}
	tx := pb.ProcessedTransaction{}
	//block := &common.Block{}
	err = proto.Unmarshal(resp.Response.Payload, &tx)
	if err != nil {
		return nil, err
	}
	fmt.Println(tx.ValidationCode)
	// fmt.Println(tx.TransactionEnvelope.Payload)
	payload, err := putils.UnmarshalPayload(tx.TransactionEnvelope.Payload)
	if err != nil {
		return nil, err
	}
	fmt.Println(payload.Header)
	return nil, nil
}